package bullgo

import (
	// "reflect"

	"github.com/go-redis/redis/v8"
)

// func apply(object interface{}, methodName string, args ...interface{}) {
// 	inputs := make([]reflect.Value, len(args))
// 	for i, val := range args {
// 		if val != "" {
// 			inputs[i] = reflect.ValueOf(val)
// 		}
// 	}
// 	reflect.ValueOf(object).MethodByName(methodName).Call(inputs)
// }

type ct struct {
	Commands []string
	Types    []string
}

func (queue *Queue) commandByTypes(
	types []string,
	count bool,
	pipe *redis.Pipeliner,
	args ...interface{},
) ct {
	commands := []string{}
	mytypes := []string{}
	for _, val := range types {
		key := ""
		switch val {
		case "completed":
			mytypes = append(mytypes, "completed")
			key = queue.queueKeys.Completed
		case "failed":
			mytypes = append(mytypes, "failed")
			key = queue.queueKeys.Failed
		case "delayed":
			mytypes = append(mytypes, "delayed")
			key = queue.queueKeys.Delayed
		case "repeat":
			mytypes = append(mytypes, "repeat")
			key = queue.queueKeys.Repeat
		case "active":
			mytypes = append(mytypes, "active")
			key = queue.queueKeys.Active
		case "wait":
			mytypes = append(mytypes, "wait")
			key = queue.queueKeys.Wait
		case "paused":
			mytypes = append(mytypes, "paused")
			key = queue.queueKeys.Paused
		}
		switch val {
		case "completed", "failed", "delayed", "repeat":
			if count {
				(*pipe).ZCard(queue.Ctx, key)
				commands = append(commands, "ZCard")
			} else {
				start := args[0].(int64)
				end := args[1].(int64)
				asc := args[2].(bool)
				if asc {
					(*pipe).ZRevRange(queue.Ctx, key, start, end)
					commands = append(commands, "ZRevRange")
				} else {
					(*pipe).ZRange(queue.Ctx, key, start, end)
					commands = append(commands, "ZRange")
				}
			}
		case "active", "wait", "paused":
			if count {
				(*pipe).LLen(queue.Ctx, key)
				commands = append(commands, "LLen")
			} else {
				start := args[0].(int64)
				end := args[1].(int64)
				asc := args[2].(bool)
				if asc {
					(*pipe).LRange(queue.Ctx, key, -(end + 1), -(start + 1))
				} else {
					(*pipe).LRange(queue.Ctx, key, start, end)
				}
				commands = append(commands, "LRange")
			}
		}
	}
	return ct{Commands: commands, Types: mytypes}
}

// GetJob ...
func (queue *Queue) GetJob(jobID string) *Job {
	job, err := newJobFromID(queue, jobID)
	if err != nil {
		return nil
	}
	return job
}

// Count ...
func (queue *Queue) Count() map[string]int64 {
	return queue.GetJobCountByTypes(Wait, Paused, Delayed)
}

// GetJobCountByTypes ...
func (queue *Queue) GetJobCountByTypes(types ...string) map[string]int64 {
	pipe := queue.Client.TxPipeline()
	countSum := map[string]int64{}

	ct := queue.commandByTypes(types, true, &pipe)
	mytypes := ct.Types

	results, _ := pipe.Exec(queue.Ctx)
	for i, val := range results {
		count, _ := val.(*redis.IntCmd).Result()
		countSum[mytypes[i]] = count
	}

	return countSum
}

// GetCompletedCount ...
func (queue *Queue) GetCompletedCount() int64 {
	return queue.GetJobCountByTypes("completed")["completed"]
}

// GetFailedCount ...
func (queue *Queue) GetFailedCount() int64 {
	return queue.GetJobCountByTypes("failed")["failed"]
}

// GetDelayedCount ...
func (queue *Queue) GetDelayedCount() int64 {
	return queue.GetJobCountByTypes("delayed")["delayed"]
}

// GetActiveCount ...
func (queue *Queue) GetActiveCount() int64 {
	return queue.GetJobCountByTypes("active")["active"]
}

// GetWaitingCount ...
func (queue *Queue) GetWaitingCount() int64 {
	count := queue.GetJobCountByTypes("wait", "paused")
	return count["wait"] + count["paused"]
}

// GetPausedCount ...
func (queue *Queue) GetPausedCount() int64 {
	return queue.GetJobCountByTypes("paused")["paused"]
}

// GetJobsRange ...
func (queue *Queue) GetJobsRange(types []string, start int64, end int64, asc bool) map[string][]*Job {
	pipe := queue.Client.TxPipeline()
	mapJobs := map[string][]*Job{}

	ct := queue.commandByTypes(types, false, &pipe, start, end, asc)
	commands := ct.Commands
	types = ct.Types
	results, _ := pipe.Exec(queue.Ctx)
	for i, val := range results {
		jobIDs, _ := val.(*redis.StringSliceCmd).Result()
		jobs := []*Job{}
		for i := 0; i < len(jobIDs); i++ {
			job := queue.GetJob(jobIDs[i])
			jobs = append(jobs, job)
		}
		mapJobs[types[i]] = jobs
	}
	if asc {
		for i, tp := range commands {
			if tp == "LRange" {
				jobs := mapJobs[types[i]]
				var temp *Job
				lens := len(jobs)
				for i := 0; i < lens/2; i++ {
					temp = jobs[i]
					jobs[i] = jobs[lens-1-i]
					jobs[lens-1-i] = temp
				}
			}
		}
	}
	return mapJobs
}

// GetJobs ...
func (queue *Queue) GetJobs(types []string, asc bool) map[string][]*Job {
	var start, end int64 = 0, -1
	return queue.GetJobsRange(types, start, end, asc)
}
